package com.hitqz.robot.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.business.enums.TicketsStatusEnum;
import com.hitqz.robot.api.business.enums.WorkStatusEnum;
import com.hitqz.robot.api.business.model.dto.FlowContextDto;
import com.hitqz.robot.api.common.dto.robot.RobotOnlineEventDto;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.entity.Tickets;
import com.hitqz.robot.api.common.entity.TicketsTask;
import com.hitqz.robot.api.dispatch.protocol.dto.CurrentTaskInfoDto;
import com.hitqz.robot.biz.flow.util.LiteFlowUtil;
import com.hitqz.robot.biz.mapper.TicketsMapper;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.biz.service.TicketsService;
import com.hitqz.robot.biz.service.TicketsTaskService;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.driver.model.IRobotPosDto;
import com.hitqz.robot.driver.parent.RobotDriverService;
import com.hitqz.robot.netty.service.Send2ServerService;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * @author xupkun
 * @date 2024/6/3
 */
@Service
@AllArgsConstructor
@Slf4j
public class TicketsServiceIml extends ServiceImpl<TicketsMapper, Tickets> implements TicketsService {
    @Resource
    private FlowExecutor flowExecutor;

    private final TicketsTaskService ticketsTaskService;

    private final RobotService robotService;

    private final RobotDriverService robotDriverService;

    private final Send2ServerService send2ServerService;

    private final ExecutorService ticketsThreadPool;

    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());

    @Override
    public R executeFlowChain(Integer ticketsId) throws ExecutionException, InterruptedException, TimeoutException {
        synchronized (this) {
            flowExecutor.reloadRule();
            IRobotPosDto iRobotPosDto = robotDriverService.getWebPos();
            Robot robot = robotService.getDefaultRobot();
            if (robot.getOnlineStatus().equals("0")) {
                log.warn("[tickets]execute id:{} failed,robot is offline", ticketsId);
                return R.failed("机器人不在线");
            }
            if (robot.getWorkStatus().equals(WorkStatusEnum.WORK.getCode())
                    || robot.getWorkStatus().equals(WorkStatusEnum.PATROL.getCode())
                    || robot.getWorkStatus().equals(WorkStatusEnum.WORK_TRANSPORT.getCode())) {
                log.warn("[tickets]execute id:{} failed,robot is now working", ticketsId);
                return R.failed("机器人当前已经在工作中");
            }
            if (robot.getWorkThreshold() > iRobotPosDto.getBatteryLevel()) {
                log.warn("[tickets]execute id:{} failed,robot power:{} < {}", ticketsId, iRobotPosDto.getBatteryLevel(), robot.getWorkThreshold());
                return R.failed("当前电量小于工作阈值:" + robot.getWorkThreshold() + "，无法执行任务");
            }
            log.info("{}状态重置", robot.getRobotSn());
            //取消充电
            String currentDirection = iRobotPosDto.getCurrentDirection();
            if (Objects.equals(currentDirection, "充电")) {
                robotDriverService.cmdCharge(0);
                Thread.sleep(3000);
                log.info("[tickets]cancel charge");
            }
            //获取工单信息
            Tickets tickets = this.getById(ticketsId);
            if (tickets == null) {
                log.warn("[tickets]can't find tickets id:{} ", ticketsId);
                return R.failed("没有找到工单" + ticketsId + "信息");
            }
            //修改机器人状态 确保工作状态下不会同时执行多个流程
            RobotOnlineEventDto robotOnlineEventDto = new RobotOnlineEventDto();
            robotOnlineEventDto.setWorkStatus(WorkStatusEnum.WORK.getCode());
            SpringUtil.getApplicationContext().publishEvent(robotOnlineEventDto);
            //记录当前的工单
            LiteFlowUtil.ticketsId = ticketsId;
            //同步工单状态
            tickets.setStatus(TicketsStatusEnum.STATUS_EXECUTING.getCode());
            this.updateById(tickets);
            send2ServerService.syncTickets(robot, tickets, ticketsTaskService.list(
                    Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId())
            ));
            //异步执行任务
            ticketsThreadPool.submit(new TicketsTaskExecuteThread(robot, tickets));
            return R.ok(ticketsId, "工单下发成功");
        }

    }

    @Override
    public boolean addTask(Tickets tickets) {
        List<TicketsTask> tasks = ticketsTaskService.list(Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId()));
        if (!tasks.isEmpty()) {
            return true;
        }
        return ticketsTaskService.createTicket(tickets.getContent(), tickets);
    }


    //处理任务的线程
    private class TicketsTaskExecuteThread implements Runnable {

        private Robot robot;
        private Tickets tickets;

        public TicketsTaskExecuteThread(Robot robot, Tickets tickets) {
            this.robot = robot;
            this.tickets = tickets;
        }

        @Override
        public void run() {
            Cache<String, CurrentTaskInfoDto> currentTaskInfo = SpringUtil.getBean("currentTaskInfo");
            try {
                @SuppressWarnings("unchecked")
                Cache<String, Object> flowCache = (Cache<String, Object>) SpringUtil.getBean("flowCache", Cache.class);
                Long ticketsId = tickets.getId();
                //获取工单任务
                List<TicketsTask> taskList = ticketsTaskService
                        .list(Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, ticketsId));
                FlowContextDto dto = new FlowContextDto();
                dto.setTickets(tickets);
                TicketsService ticketsService = SpringUtil.getBean(TicketsService.class);
                for (TicketsTask task : CollUtil.emptyIfNull(taskList)) {
                    //确保都是工作状态
                    robot = robotService.getById(robot.getId());
                    robot.setWorkStatus(WorkStatusEnum.WORK.getCode());
                    robotService.updateById(robot);
                    //设置当前作业任务信息
                    currentTaskInfo.put(robot.getRobotSn(), CurrentTaskInfoDto.builder()
                                    .taskNo(tickets.getTaskNo())
                                    .taskParams(task.getParseOrderParams())
                                    .build());

                    dto.setTicketsTask(task);
                    dto.setParams(task.getParseOrderParams());
                    Future<LiteflowResponse> responseFuture = flowExecutor.execute2Future(task.getFlowName(), dto);
                    //保存信息 后续可以通过cancel和标志位终止
                    LiteFlowUtil.future = responseFuture;
                    log.info("[tickets-task]execute id:{}", task.getId());
                    task.setStatus(TicketsStatusEnum.TICKETS_TASK_EXECUTING.getCode());
                    ticketsTaskService.updateById(task);
                    //处理工单任务回调结果
                    try {
                        LiteflowResponse liteflowResponse = responseFuture.get(60, TimeUnit.MINUTES);
                        if (liteflowResponse.isSuccess()) {
                            //执行成功
                            log.info("{} [tickets-task]execute id:{} finish,success", task.getFlowName(), task.getId());
                            task.setFinishTime(new Date());
                            task.setStatus(TicketsStatusEnum.TICKETS_TASK_FINISHED.getCode());
                            ticketsTaskService.updateById(task);
                            log.info("{} [tickets-task]finish id:{} finish", task.getFlowName(), task.getId());
                        } else {
                            //执行失败
                            log.info("[tickets-task]execute id:{} finish,failed,cause:{}", task.getId(), liteflowResponse.getCause().getMessage());
                            task.setStatus(TicketsStatusEnum.TICKETS_TASK_STOP.getCode());
                            ticketsTaskService.updateById(task);
                            tickets.setStatus(TicketsStatusEnum.STATUS_FAIL.getCode());

                            AddWarningDto addWarningDto = new AddWarningDto();
                            addWarningDto.setCode("0");
                            addWarningDto.setTitle("任务执行异常");
                            addWarningDto.setContent(liteflowResponse.getCause().getMessage());
                            addWarningDto.setSuggestion("请排查");
                            addWarningDto.setLevel(1);
                            SpringUtil.getApplicationContext().publishEvent(addWarningDto);
                            break;
                        }
                    } catch (Exception e) {
                        log.info("[tickets-task]execute id:{} error,cause:", task.getId(), e);
                        task.setStatus(TicketsStatusEnum.TICKETS_TASK_STOP.getCode());
                        ticketsTaskService.updateById(task);
                        tickets.setStatus(TicketsStatusEnum.STATUS_FAIL.getCode());
                        AddWarningDto addWarningDto = new AddWarningDto();
                        addWarningDto.setCode("0");
                        addWarningDto.setTitle("任务执行异常");
                        addWarningDto.setContent(e.getMessage());
                        addWarningDto.setSuggestion("请排查");
                        addWarningDto.setLevel(1);
                        SpringUtil.getApplicationContext().publishEvent(addWarningDto);
                        break;
                    } finally {
                        //同步工单任务信息
                        tickets = ticketsService.getById(tickets.getId());
                        if (tickets.getServerId() != null) {
                            send2ServerService.syncTickets(robot, tickets, ticketsTaskService.list(
                                    Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId())
                            ));
                        }
                    }
                }
                //更新工单状态
                tickets.setAllotRobotName(robot.getRobotName());
                tickets.setAllotRobotSn(robot.getRobotSn());
                if (!Objects.equals(tickets.getStatus(), TicketsStatusEnum.STATUS_FAIL.getCode())) {
                    tickets.setStatus(TicketsStatusEnum.STATUS_FINISHED.getCode());
                }
                tickets.setFinishTime(new Date());
                ticketsService.updateById(tickets);
                //同步工单状态
                send2ServerService.syncTickets(robot, tickets, ticketsTaskService.list(
                        Wrappers.<TicketsTask>lambdaQuery().eq(TicketsTask::getTicketsId, tickets.getId())
                ));
                log.info("[tickets]id:{} execute finish", ticketsId);
            } finally {
                currentTaskInfo.invalidateAll();
                //更新机器人状态
                log.info("任务线程，处理任务为空闲");
                RobotOnlineEventDto finishDto = new RobotOnlineEventDto();
                finishDto.setWorkStatus(WorkStatusEnum.FREE.getCode());
                SpringUtil.getApplicationContext().publishEvent(finishDto);
            }

        }

    }


}
